import { Component, director, game, JsonAsset, Node, Size, size, sys, tween, UIOpacity, UITransform, v3, Vec2, Vec3, view } from "cc";
import assetsMgr from "./core/AssetsMgr";
import audioManager from "./core/AudioMgr";
import { ButtonFlag, EventConst, LocalKey, Location, Singleton } from "./core/Common";
import userEvent from "./core/Event";
import { i18nMgr } from "./core/i18nMgr";
import NativeManager from "./core/NativeMgr";
import sceneMgr from "./core/SceneMgr";
import stringTool from "./core/StringUtil";
import utilTool from "./core/UtilTool";
import viewManager from "./core/ViewMgr";

class app extends Singleton<app>{
    private _language:string = 'zh';
    private _config:any = null;
    private _localData:any = {};
    private _isSingle:boolean = true; // 是否单场景，即项目中有且仅有一个 scene 文件
    protected onInit(persistNode:Node,config:string,call:()=>void): void {
        game.addPersistRootNode(persistNode);
        this.Asset.load(config,(json:JsonAsset)=>{
            const CFG:any = json.json;

            this._config = CFG;
            const storage = sys.localStorage.getItem(CFG.project) || null;
            !this.isEmpty(storage) && (this._localData = JSON.parse(this.String.decode64(storage)));
            this._language = this.getLocalStorage(LocalKey.LANG) || CFG.language.lang;
            this._isSingle = CFG.scenes.single;

            this.Event.init();
            this.String.init();
            this.Util.init();
            this.Native.init();
            this.Asset.init();
            this.i18n.init(CFG.language);
            App.Audio.init(persistNode);
            this.Scene.init(persistNode);
            this.View.init(persistNode);
            call && call();
        },JsonAsset);
        
        // 重启的时候必须清理下
        this.Event.on(EventConst.APP_RESTART,()=>{
            this.Scene.destroy();
            this.View.destroy();
            this.Asset.destroy();
            this.Audio.destroy();
            this.Event.destroy();
        },this);
    }

    public set language(val){this._language = val;}
    public get language():string{return this._language;}
    public get singleScene():boolean{return this._isSingle;}

    public get Util():utilTool{return utilTool.getInstance<utilTool>(utilTool);}
    public get i18n():i18nMgr{return i18nMgr.getInstance<i18nMgr>(i18nMgr);}
    public get Event():userEvent{return userEvent.getInstance<userEvent>(userEvent);}
    public get Asset():assetsMgr{return assetsMgr.getInstance<assetsMgr>(assetsMgr);}
    public get Scene():sceneMgr{return sceneMgr.getInstance<sceneMgr>(sceneMgr);}
    public get Audio():audioManager{return audioManager.getInstance<audioManager>(audioManager);}
    public get String():stringTool{return stringTool.getInstance<stringTool>(stringTool);}
    public get Native():NativeManager{return NativeManager.getInstance<NativeManager>(NativeManager);}
    public get View():viewManager{return viewManager.getInstance<viewManager>(viewManager);}

    public setLocalStorage(key:string,data:any){
        if (!key || key.length < 0) 
            this._localData = data;
        else this._localData[key] = data;
        let _str = JSON.stringify(this._localData);//this.String.encode64(this._localData);
        sys.localStorage.setItem(this.getValue('project'),_str);
    }

    public getLocalStorage(key:string,def?:any):any{
        const data:any = !this.isEmpty(key)&&this._localData[key];
        this.isInvalid(data) && !this.isInvalid(def) && this.setLocalStorage(key,def);
        return data || def;
    }

    public getValue(key:string):any{
        if (this.isEmpty(key)) return this._config;
        let _paths = key.split('/');

        let _val:any = this._config[_paths.shift()];
        while (_paths.length > 0) {
            if (!_val) return _val;
            _val = _val[_paths.shift()];
        }

        if (this.isInvalid(_val)){
            const not = `WARN:App.getCfg(${key}) is invalid.`;
            console.warn(not);
        }

        return _val;
    }

    public runScene(name:string,...args:any){
        this.Event.emit(EventConst.ENTRY_SCENE,name,args);
    }

    public setMainView(name:string,...args:any){
        this.Event.emit(EventConst.SET_MAIN_VIEW,name,args);
    }

    public openView(name:string,...args:any){
        this.Event.emit(EventConst.POP_VIEW,name,args);
    }

    public closeView(view:Node|Component){
        this.Event.emit(EventConst.CLOSE_VIEW,view);
    }

    public showMessageBox(text:string,call?:(flag:ButtonFlag)=>void){
        this.Event.emit(EventConst.POP_MSG_BOX,text,call);
    };

    public popTip(text:string,location:Location = Location.Top){
        this.Util.isEmpty(text) && this.Event.emit(EventConst.POP_TIPS,text,location);
    }
    // 对象是否有效
    public isInvalid<T>(obj:T):boolean{
        return this.Util.isInvalid(obj);
    };
    
    /**
     * 字符串是否为空
     * @param str 字符串
     * @param type 去空格标识
     * @returns 
     */
    public isEmpty(str: string, type: number = 0): boolean { 
        return this.Util.isEmpty(str,type);
    };

    public locationToPos(local?:Location):Vec3{
        const vs:Size = view.getVisibleSize();
        const item:Size = size(Math.floor(0.6*vs.width),Math.floor(0.6*vs.height));
        switch (local){
            case Location.LeftTop:return v3(-1*item.width,item.height,0);
            case Location.Top:return v3(0,item.height,0);
            case Location.RightTop:return v3(item.width,item.height,0);
            case Location.Left:return v3(-1*item.width,0,0);
            case Location.Center:return v3(0,0,0);
            case Location.Right:return v3(item.width,0,0);
            case Location.LeftBottom:return v3(-1*item.width,-1*item.height,0);
            case Location.Bottom:return v3(0,-1*item.height,0);
            case Location.RightBottom:return v3(item.width,-1*item.height,0);
            default: return v3(item.width,item.height,0);
        }
    }

    public opacity(obj:Node|Component,opacity:number,delay:number=0,call?:()=>void):UIOpacity{
        if (!this.isInvalid(obj)){
            const opa:UIOpacity = obj.getComponent(UIOpacity) || obj.addComponent(UIOpacity);
            tween(opa).stop();
            tween(opa).to(delay,{opacity:opacity},{easing:opa.opacity > opacity ? 'quadOut':'quadIn'})
            .call(()=>{call && call();}).start();
            return opa;
        }
        return null;
    }

    public scale(obj:Node|Component,scaleX:number,scaleY?:number,scaleZ?:number):Node{
        if (!this.isInvalid(obj)){
            let tmp:any = obj;
            if (obj instanceof Component) tmp = obj.node;
            tmp.scale =v3(scaleX,scaleY||scaleX,scaleZ||scaleX);
            return tmp;
        }
        return null;
    }

    public position(obj:Node|Component,x:number,y?:number,z?:number):Node{
        if (!this.isInvalid(obj)){
            let tmp:any = obj;
            if (obj instanceof Component) tmp = obj.node;
            tmp.position=v3(x,y||x,z||x);
            return tmp;
        }
        return null;
    }

    public contentSize(obj:Node|Component,w?:Size|number,h?:number):Size{
        if (!this.isInvalid(obj)){
            // @ts-ignore
            let width = w as number || 0;
            let height = h;
            if (w && typeof w === 'object') {
                width = w.width;
                height = w.height;
            }
            const ui:UITransform = obj.getComponent(UITransform) || obj.addComponent(UITransform);
            (!isNaN(width) && !isNaN(height)) && (ui.contentSize = size(width,height));
            return ui.contentSize;
        }
        return size(0,0);
    }
}

export const App:app = app.getInstance<app>(app);